<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>几何图形分类工具</title>
  <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
  <style>
    body {
      font-family: 'Arial', sans-serif;
      background-color: #f5f5f5;
      margin: 0;
      padding: 20px;
    }
  </style>
</head>
<body>
  <div id="root"></div>
  
  <script type="text/babel">
    const ShapeClassifier = () => {
      // 状态管理
      const [stage, setStage] = React.useState('start'); // 'start', 'classify', 'result'
      const [shapes, setShapes] = React.useState([]);
      const [classificationSteps, setClassificationSteps] = React.useState([]);
      const [currentStep, setCurrentStep] = React.useState(1);
      const [leftBoxShapes, setLeftBoxShapes] = React.useState([]);
      const [rightBoxShapes, setRightBoxShapes] = React.useState([]);

      // 初始化可爱图形
      React.useEffect(() => {
        setShapes([
          { id: 'square', name: '正方形', type: '2d', path: [] },
          { id: 'rectangle', name: '长方形', type: '2d', path: [] },
          { id: 'circle', name: '圆形', type: '2d', path: [] },
          { id: 'triangle', name: '三角形', type: '2d', path: [] },
          { id: 'cube', name: '正方体', type: '3d', path: [] },
          { id: 'cuboid', name: '长方体', type: '3d', path: [] },
          { id: 'cylinder', name: '圆柱', type: '3d', path: [] },
          { id: 'sphere', name: '球体', type: '3d', path: [] }
        ]);
      }, []);

      // 开始分类
      const startClassification = () => {
        setStage('classify');
        setLeftBoxShapes([]);
        setRightBoxShapes([]);
        setClassificationSteps([]);
        setCurrentStep(1);
      };

      // 处理拖放
      const handleDrop = (boxSide, e) => {
        e.preventDefault();
        const shapeId = e.dataTransfer.getData('shapeId');
        const shape = shapes.find(s => s.id === shapeId);
        
        if (!shape) return;
        
        // 从原位置移除
        setShapes(shapes.filter(s => s.id !== shapeId));
        
        // 添加到目标框
        if (boxSide === 'left') {
          setLeftBoxShapes([...leftBoxShapes, shape]);
        } else {
          setRightBoxShapes([...rightBoxShapes, shape]);
        }
      };

      // 确认分类
      const confirmClassification = () => {
        // 只有当两个框都有形状时才能继续
        if (leftBoxShapes.length === 0 || rightBoxShapes.length === 0) {
          alert('请确保两个框中都放入了至少一个图形！');
          return;
        }
        
        // 保存当前分类步骤
        const step = {
          id: currentStep,
          leftShapes: [...leftBoxShapes],
          rightShapes: [...rightBoxShapes]
        };
        
        setClassificationSteps([...classificationSteps, step]);
        
        // 准备下一步分类
        if (leftBoxShapes.length > 1) {
          // 如果左框有多个图形，下一步分类它们
          setShapes(leftBoxShapes);
          setLeftBoxShapes([]);
          setRightBoxShapes([]);
          setCurrentStep(currentStep + 1);
        } else if (rightBoxShapes.length > 1) {
          // 如果右框有多个图形，下一步分类它们
          setShapes(rightBoxShapes);
          setLeftBoxShapes([]);
          setRightBoxShapes([]);
          setCurrentStep(currentStep + 1);
        } else {
          // 如果两个框都只有一个图形，分类完成
          setStage('result');
        }
      };

      // 完成分类
      const finishClassification = () => {
        // 保存最后一步分类
        if (leftBoxShapes.length > 0 || rightBoxShapes.length > 0) {
          const step = {
            id: currentStep,
            leftShapes: [...leftBoxShapes],
            rightShapes: [...rightBoxShapes]
          };
          setClassificationSteps([...classificationSteps, step]);
        }
        setStage('result');
      };

      // 重新开始
      const restart = () => {
        setStage('start');
        setClassificationSteps([]);
        setCurrentStep(1);
        // 重新初始化图形
        setShapes([
          { id: 'square', name: '正方形', type: '2d', path: [] },
          { id: 'rectangle', name: '长方形', type: '2d', path: [] },
          { id: 'circle', name: '圆形', type: '2d', path: [] },
          { id: 'triangle', name: '三角形', type: '2d', path: [] },
          { id: 'cube', name: '正方体', type: '3d', path: [] },
          { id: 'cuboid', name: '长方体', type: '3d', path: [] },
          { id: 'cylinder', name: '圆柱', type: '3d', path: [] },
          { id: 'sphere', name: '球体', type: '3d', path: [] }
        ]);
      };

      // 渲染可爱的图形
      const renderShape = (shape, size = 'normal') => {
        const sizes = {
          small: { width: 40, height: 40, container: 50 },
          normal: { width: 60, height: 60, container: 80 },
          tiny: { width: 30, height: 30, container: 40 }
        };
        
        const { width, height, container } = sizes[size];
        
        const shapeStyle = {
          display: 'inline-flex',
          justifyContent: 'center',
          alignItems: 'center',
          margin: size === 'tiny' ? '5px' : '10px',
          cursor: size === 'tiny' ? 'default' : 'move',
          backgroundColor: '#f8f8f8',
          borderRadius: '10px',
          padding: '5px',
          width: `${container}px`,
          height: `${container}px`,
          boxShadow: '0 4px 8px rgba(0,0,0,0.1)',
          transition: 'transform 0.2s',
        };

        const renderCuteShape = () => {
          switch (shape.id) {
            case 'square':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <rect x="5" y="5" width="50" height="50" rx="2" ry="2" fill="#FF9AA2" stroke="#FF6B6B" strokeWidth="2" />
                  <circle cx="20" cy="25" r="3" fill="#333" />
                  <circle cx="40" cy="25" r="3" fill="#333" />
                  <path d="M25 35 Q30 40 35 35" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            case 'rectangle':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <rect x="5" y="15" width="50" height="30" rx="3" ry="3" fill="#FFDAC1" stroke="#E2B091" strokeWidth="2" />
                  <circle cx="20" cy="25" r="3" fill="#333" />
                  <circle cx="40" cy="25" r="3" fill="#333" />
                  <path d="M25 35 Q30 38 35 35" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            case 'circle':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <circle cx="30" cy="30" r="25" fill="#B5EAD7" stroke="#8ED1BD" strokeWidth="2" />
                  <circle cx="22" cy="25" r="4" fill="#333" />
                  <circle cx="38" cy="25" r="4" fill="#333" />
                  <path d="M22 38 Q30 45 38 38" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            case 'triangle':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <polygon points="30,5 55,50 5,50" fill="#C7CEEA" stroke="#9DA5D6" strokeWidth="2" />
                  <circle cx="22" cy="30" r="3" fill="#333" />
                  <circle cx="38" cy="30" r="3" fill="#333" />
                  <path d="M25 40 Q30 43 35 40" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            case 'cube':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <polygon points="15,15 45,15 45,45 15,45" fill="#E0AAFF" stroke="#C77DFF" strokeWidth="2" />
                  <polygon points="25,5 55,5 45,15 15,15" fill="#E9C8FF" stroke="#C77DFF" strokeWidth="2" />
                  <polygon points="45,15 55,5 55,35 45,45" fill="#BE95FF" stroke="#C77DFF" strokeWidth="2" />
                  <circle cx="30" cy="30" r="3" fill="#333" />
                  <circle cx="40" cy="20" r="3" fill="#333" />
                  <path d="M28 38 Q32 42 36 38" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            case 'cuboid':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <polygon points="10,20 50,20 50,40 10,40" fill="#FFB3C6" stroke="#FF8FAB" strokeWidth="2" />
                  <polygon points="20,10 60,10 50,20 10,20" fill="#FFC9D9" stroke="#FF8FAB" strokeWidth="2" />
                  <polygon points="50,20 60,10 60,30 50,40" fill="#FF96B8" stroke="#FF8FAB" strokeWidth="2" />
                  <circle cx="25" cy="30" r="3" fill="#333" />
                  <circle cx="40" cy="30" r="3" fill="#333" />
                  <path d="M26 36 Q32 40 38 36" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            case 'cylinder':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <ellipse cx="30" cy="15" rx="20" ry="10" fill="#A0C4FF" stroke="#7B9FEA" strokeWidth="2" />
                  <rect x="10" y="15" width="40" height="30" fill="#A0C4FF" stroke="#7B9FEA" strokeWidth="2" />
                  <ellipse cx="30" cy="45" rx="20" ry="10" fill="#8EB6FF" stroke="#7B9FEA" strokeWidth="2" />
                  <circle cx="22" cy="25" r="3" fill="#333" />
                  <circle cx="38" cy="25" r="3" fill="#333" />
                  <path d="M25 35 Q30 38 35 35" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            case 'sphere':
              return (
                <svg width={width} height={height} viewBox="0 0 60 60">
                  <circle cx="30" cy="30" r="25" fill="#FFC6FF" stroke="#FFA6FE" strokeWidth="2" />
                  <ellipse cx="30" cy="30" rx="25" ry="15" fill="none" stroke="#FFA6FE" strokeWidth="1" />
                  <ellipse cx="30" cy="30" rx="15" ry="25" fill="none" stroke="#FFA6FE" strokeWidth="1" />
                  <circle cx="22" cy="25" r="4" fill="#333" />
                  <circle cx="38" cy="25" r="4" fill="#333" />
                  <path d="M22 38 Q30 45 38 38" stroke="#333" strokeWidth="2" fill="none" />
                </svg>
              );
            default:
              return null;
          }
        };

        // 使用 React.Fragment 替代 <> 片段语法
        const content = (
          <React.Fragment>
            {renderCuteShape()}
            {size !== 'tiny' && (
              <div style={{ 
                marginTop: '3px', 
                textAlign: 'center', 
                fontSize: size === 'small' ? '10px' : '12px', 
                fontWeight: 'bold', 
                color: '#555' 
              }}>
                {shape.name}
              </div>
            )}
          </React.Fragment>
        );
        
        // 如果是tiny尺寸，不需要拖拽功能
        if (size === 'tiny') {
          return (
            <div key={`${shape.id}-${Math.random()}`} style={shapeStyle}>
              {content}
            </div>
          );
        }

        return (
          <div
            key={`${shape.id}-${Math.random()}`}
            draggable
            onDragStart={(e) => {
              e.dataTransfer.setData('shapeId', shape.id);
            }}
            style={shapeStyle}
          >
            {content}
          </div>
        );
      };

      // 渲染拖放框
      const renderDropBox = (side) => {
        const boxStyle = {
          width: '45%',
          minHeight: '200px',
          border: '2px dashed #ccc',
          borderRadius: '15px',
          padding: '10px',
          display: 'flex',
          flexWrap: 'wrap',
          justifyContent: 'center',
          alignItems: 'center',
          backgroundColor: side === 'left' ? '#fff6e6' : '#e6f7ff',
          transition: 'background-color 0.3s'
        };

        return (
          <div
            style={boxStyle}
            onDragOver={(e) => e.preventDefault()}
            onDrop={(e) => handleDrop(side, e)}
          >
            <div style={{ width: '100%' }}>
              <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center' }}>
                {side === 'left' 
                  ? leftBoxShapes.map(shape => renderShape(shape))
                  : rightBoxShapes.map(shape => renderShape(shape))
                }
                {(side === 'left' ? leftBoxShapes.length === 0 : rightBoxShapes.length === 0) && (
                  <div style={{ color: '#aaa', fontSize: '14px', padding: '20px', textAlign: 'center' }}>
                    将图形拖到这里
                  </div>
                )}
              </div>
            </div>
          </div>
        );
      };

      // 渲染垂直的分类树
      const renderVerticalTree = () => {
        // 构建分类树数据
        const buildTreeData = () => {
          const allShapes = [
            { id: 'square', name: '正方形', type: '2d' },
            { id: 'rectangle', name: '长方形', type: '2d' },
            { id: 'circle', name: '圆形', type: '2d' },
            { id: 'triangle', name: '三角形', type: '2d' },
            { id: 'cube', name: '正方体', type: '3d' },
            { id: 'cuboid', name: '长方体', type: '3d' },
            { id: 'cylinder', name: '圆柱', type: '3d' },
            { id: 'sphere', name: '球体', type: '3d' }
          ];
          
          // 创建根节点
          const rootNode = {
            id: 'root',
            shapes: allShapes,
            children: []
          };
          
          // 根据分类步骤构建树
          const buildTree = (node, steps, depth = 0) => {
            if (depth >= steps.length) return;
            
            const currentStep = steps[depth];
            const leftShapeIds = currentStep.leftShapes.map(s => s.id);
            const rightShapeIds = currentStep.rightShapes.map(s => s.id);
            
            // 过滤出当前节点中属于左右分支的图形
            const leftBranchShapes = node.shapes.filter(s => leftShapeIds.includes(s.id));
            const rightBranchShapes = node.shapes.filter(s => rightShapeIds.includes(s.id));
            
            // 创建左右子节点
            if (leftBranchShapes.length > 0) {
              const leftNode = {
                id: `${depth}-left`,
                shapes: leftBranchShapes,
                children: []
              };
              node.children.push(leftNode);
              
              // 如果左分支有多个图形，继续构建子树
              if (leftBranchShapes.length > 1) {
                buildTree(leftNode, steps, depth + 1);
              }
            }
            
            if (rightBranchShapes.length > 0) {
              const rightNode = {
                id: `${depth}-right`,
                shapes: rightBranchShapes,
                children: []
              };
              node.children.push(rightNode);
              
              // 如果右分支有多个图形，继续构建子树
              if (rightBranchShapes.length > 1) {
                // 查找下一个适用于右分支的分类步骤
                let nextDepth = depth + 1;
                while (nextDepth < steps.length) {
                  const nextStep = steps[nextDepth];
                  const nextStepShapeIds = [...nextStep.leftShapes.map(s => s.id), ...nextStep.rightShapes.map(s => s.id)];
                  
                  // 检查右分支的图形是否匹配下一步
                  const isMatch = rightBranchShapes.every(s => nextStepShapeIds.includes(s.id));
                  if (isMatch) {
                    buildTree(rightNode, steps, nextDepth);
                    break;
                  }
                  nextDepth++;
                }
              }
            }
          };
          
          buildTree(rootNode, classificationSteps);
          return rootNode;
        };
        
        // 渲染树节点
        const renderTreeNode = (node, level = 0, isRoot = true) => {
          const containerStyle = {
            display: 'flex',
            flexDirection: 'column',
            alignItems: 'center',
            width: '100%',
            marginBottom: isRoot ? '0' : '15px'
          };
          
          const nodeStyle = {
            display: 'flex',
            justifyContent: 'center',
            flexWrap: 'wrap',
            padding: '10px',
            borderRadius: '10px',
            backgroundColor: isRoot ? '#f0f0f0' : '#fff',
            boxShadow: isRoot ? '0 3px 6px rgba(0,0,0,0.1)' : 'none',
            marginBottom: '10px'
          };
          
          const branchesStyle = {
            display: 'flex',
            justifyContent: 'center',
            width: '100%',
            position: 'relative'
          };
          
          // 渲染当前节点的所有图形
          const shapes = node.shapes.map(shape => 
            renderShape(shape, node.children.length > 0 ? 'small' : 'normal')
          );
          
          return (
            <div style={containerStyle}>
              <div style={nodeStyle}>
                {shapes}
              </div>
              
              {node.children.length > 0 && (
                <React.Fragment>
                  <div style={{ width: '2px', height: '20px', backgroundColor: '#aaa' }}></div>
                  
                  <div style={branchesStyle}>
                    {node.children.map((child, index) => {
                      const branchWidth = 100 / node.children.length;
                      
                      return (
                        <div 
                          key={`branch-${child.id}`} 
                          style={{ 
                            width: `${branchWidth}%`,
                            maxWidth: '300px',
                            position: 'relative',
                            display: 'flex',
                            flexDirection: 'column',
                            alignItems: 'center'
                          }}
                        >
                          <div style={{ width: '60%', height: '2px', backgroundColor: '#aaa', marginBottom: '10px' }}></div>
                          {renderTreeNode(child, level + 1, false)}
                        </div>
                      );
                    })}
                  </div>
                </React.Fragment>
              )}
            </div>
          );
        };
        
        const treeData = buildTreeData();
        
        return (
          <div style={{ padding: '15px', backgroundColor: '#f9f9f9', borderRadius: '8px' }}>
            {renderTreeNode(treeData)}
          </div>
        );
      };

      // 渲染开始页面
      const renderStartView = () => {
        return (
          <div style={{ textAlign: 'center', padding: '20px' }}>
            <h2 style={{ marginBottom: '20px', color: '#333', fontWeight: 'bold' }}>几何图形分类</h2>
            <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', marginBottom: '30px' }}>
              {shapes.map(shape => renderShape(shape))}
            </div>
            <button 
              onClick={startClassification}
              style={{
                padding: '12px 25px',
                backgroundColor: '#4CAF50',
                color: 'white',
                border: 'none',
                borderRadius: '25px',
                fontSize: '16px',
                cursor: 'pointer',
                boxShadow: '0 4px 8px rgba(0,0,0,0.2)',
                transition: 'all 0.3s'
              }}
            >
              开始分类
            </button>
          </div>
        );
      };

      // 渲染分类页面
      const renderClassifyView = () => {
        return (
          <div style={{ padding: '15px' }}>
            <h3 style={{ textAlign: 'center', marginBottom: '15px', color: '#333' }}>
              第 {currentStep} 步：将图形分成两组
            </h3>
            
            <div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', marginBottom: '20px' }}>
              {shapes.map(shape => renderShape(shape))}
              {shapes.length === 0 && (
                <div style={{ 
                  padding: '15px', 
                  backgroundColor: '#e8f5e9', 
                  borderRadius: '8px', 
                  color: '#2e7d32', 
                  textAlign: 'center',
                  width: '100%',
                  marginBottom: '10px'
                }}>
                  所有图形已经分类完成！
                </div>
              )}
            </div>
            
            <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '20px' }}>
              {renderDropBox('left')}
              {renderDropBox('right')}
            </div>
            
            <div style={{ textAlign: 'center', marginTop: '20px' }}>
              {shapes.length === 0 ? (
                <button 
                  onClick={confirmClassification}
                  style={{
                    padding: '10px 20px',
                    backgroundColor: '#2196F3',
                    color: 'white',
                    border: 'none',
                    borderRadius: '25px',
                    fontSize: '14px',
                    cursor: 'pointer',
                    boxShadow: '0 2px 5px rgba(0,0,0,0.2)',
                    marginRight: '10px'
                  }}
                >
                  确认分类
                </button>
              ) : (
                <div style={{ 
                  padding: '10px', 
                  color: '#f57c00', 
                  fontSize: '14px',
                  borderRadius: '5px',
                  backgroundColor: '#fff3e0',
                  display: 'inline-block'
                }}>
                  请将所有图形分类到两个框中
                </div>
              )}
              
              {shapes.length === 0 && (
                <button 
                  onClick={finishClassification}
                  style={{
                    padding: '10px 20px',
                    backgroundColor: '#FF5722',
                    color: 'white',
                    border: 'none',
                    borderRadius: '25px',
                    fontSize: '14px',
                    cursor: 'pointer',
                    boxShadow: '0 2px 5px rgba(0,0,0,0.2)'
                  }}
                >
                  完成所有分类
                </button>
              )}
            </div>
          </div>
        );
      };

      // 渲染结果页面
      const renderResultView = () => {
        return (
          <div style={{ padding: '15px' }}>
            <h2 style={{ textAlign: 'center', marginBottom: '20px', color: '#333', fontWeight: 'bold' }}>分类完成!</h2>
            
            <div style={{ marginBottom: '20px' }}>
              <h3 style={{ marginBottom: '10px', color: '#333', fontWeight: 'bold' }}>分类树:</h3>
              {renderVerticalTree()}
            </div>
            
            <div style={{ textAlign: 'center', marginTop: '20px' }}>
              <button 
                onClick={restart}
                style={{
                  padding: '10px 20px',
                  backgroundColor: '#FF5722',
                  color: 'white',
                  border: 'none',
                  borderRadius: '25px',
                  fontSize: '14px',
                  cursor: 'pointer',
                  boxShadow: '0 2px 5px rgba(0,0,0,0.2)'
                }}
              >
                重新开始
              </button>
            </div>
          </div>
        );
      };

      // 根据当前阶段渲染不同视图
      const renderView = () => {
        switch (stage) {
          case 'start':
            return renderStartView();
          case 'classify':
            return renderClassifyView();
          case 'result':
            return renderResultView();
          default:
            return renderStartView();
        }
      };

      return (
        <div style={{ 
          maxWidth: '800px', 
          margin: '0 auto', 
          padding: '20px',
          backgroundColor: 'white',
          borderRadius: '15px',
          boxShadow: '0 5px 15px rgba(0,0,0,0.1)'
        }}>
          {renderView()}
        </div>
      );
    };

    // 渲染应用
    ReactDOM.render(<ShapeClassifier />, document.getElementById('root'));
  </script>
</body>
</html>